package com.example.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.api.annotations.dict.Dict;
import com.example.api.annotations.dict.DictObj;
import com.example.api.entity.biz.SysDict;
import com.example.mapper.DictEntryMapper;
import com.example.util.RedisClient;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


@Aspect
@Component
@Slf4j
/**
 * 使用aop切面对对象里面添加的注解做字典翻译
 * 如@Dict(dictType = "gender") gender为字典表里面设置的值
 */
public class DictConvertAspect {

    @Autowired
    private RedisClient redisUtils;

    @Autowired
    private DictEntryMapper dictEntryMapper;

    // 扫描controller，并且有DictConvert注解
    @Pointcut("@annotation(com.example.api.annotations.dict.DictConvert)")
    public void dict() {
    }

    @Around("dict()")
    public <T> Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("Into Aspect........................");
        Object responseDto = proceedingJoinPoint.proceed();
        if (responseDto instanceof List) {
            return this.listConvert(responseDto);
        } else {
            return this.singleConvert(responseDto);
        }
    }

    @SuppressWarnings("unchecked")
    private <T> Object listConvert(Object object) throws JsonProcessingException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {

        List<?> items = (List<?>) object;
        Object item;
        List<T> newList = new ArrayList<>();
        for (int i = 0; i < items.size(); i++) {
            item = items.get(i);
            Class<? extends Object> clazz = item.getClass();
            // 转换报文
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(item);
            JSONObject jsonObject = JSONObject.parseObject(json);

            // 获取最外层所有字段
            List<Field> fields = Arrays.asList(clazz.getDeclaredFields());

            for (Field field : fields) {
                DictObj dictObj = field.getAnnotation(DictObj.class);
                if (null != dictObj) {
                    // 如果是内层需要转换字段，则继续递归判断
                    String fieldName = field.getName();
                    Object fieldObject = jsonObject.get(fieldName);
                    Object convertObject = null;
                    Object subObject = null;
                    if (fieldObject instanceof List) {
                        // 获取该字段对应的实体类
                        subObject = this.getFieldObject(object, fieldName);
                        convertObject = this.listConvert(subObject);
                    } else {
                        // 获取该字段对应的实体类
                        subObject = this.getFieldObject(object, fieldName);
                        convertObject = this.singleConvert(subObject);
                    }
                    jsonObject.put(fieldName, convertObject);
                }
                Dict dict = field.getAnnotation(Dict.class);
                if (null != dict) {
                    String fieldName = field.getName();
                    String value = this.dictTranslate(dict, jsonObject, fieldName);
                    jsonObject.put(fieldName, value);
                }
            }
            item = JSON.toJavaObject(jsonObject, clazz);
            newList.add(i, (T) item);
        }
        return newList;
    }

    private <T> Object singleConvert(Object object) throws JsonProcessingException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {

        Class<? extends Object> clazz = object.getClass();

        // 转换报文
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(object);
        JSONObject jsonObject = JSONObject.parseObject(json);

        // 获取最外层所有字段
        List<Field> fields = Arrays.asList(clazz.getDeclaredFields());

        for (Field field : fields) {
            DictObj dictObj = field.getAnnotation(DictObj.class);
            if (null != dictObj) {
                // 如果是内层需要转换字段，则继续递归判断
                String fieldName = field.getName();
                Object fieldObject = jsonObject.get(fieldName);

                Object convertObject = null;
                Object subObject = null;
                if (fieldObject instanceof List) {
                    // 获取该字段对应的实体类
                    subObject = this.getFieldObject(object, fieldName);
                    convertObject = this.listConvert(subObject);
                } else {
                    // 获取该字段对应的实体类
                    subObject = this.getFieldObject(object, fieldName);
                    convertObject = this.singleConvert(subObject);
                }
                jsonObject.put(fieldName, convertObject);
            }
            Dict dict = field.getAnnotation(Dict.class);
            if (null != dict) {
                String fieldName = field.getName();
                String value = this.dictTranslate(dict, jsonObject, fieldName);
                jsonObject.put(fieldName, value);
            }
        }
        return JSON.toJavaObject(jsonObject, clazz);
    }

    /**
     * 获取报文字段对应的实体类
     *
     * @param o
     * @param fieldName
     * @return
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    private Object getFieldObject(Object o, String fieldName)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Object subObject = null;
        Method[] m = o.getClass().getMethods();
        for (int i = 0; i < m.length; i++) {
            if (("get" + fieldName).toLowerCase().equals(m[i].getName().toLowerCase())) {
                subObject = m[i].invoke(o);
            }
        }
        return subObject;
    }

    /**
     * 通过字段类型+字典值确定唯一
     *
     * @param dict
     * @param jsonObject
     * @param fieldName
     * @return
     */
    private String dictTranslate(Dict dict, JSONObject jsonObject, String fieldName) {
        String dictType = dict.dictType();
        // 获取属性值
        String key = String.valueOf(jsonObject.get(fieldName));
        // redis的key是字典类型与Code拼接而成
        String redisKey = dictType + "-" + key;
        // 先从缓存获取
        SysDict dictEntry = (SysDict) redisUtils.get(redisKey);
        String value = key;
        if (null != dictEntry) {
            value = dictEntry.getName();
            log.info("从缓存中获取");
        } else {
            // 缓存不存在，取数据库，并存入缓存
            dictEntry = dictEntryMapper.selectOne(Wrappers.<SysDict>lambdaQuery().eq(SysDict::getDictTypeId, dictType).eq(SysDict::getCode, key));
            log.info("从数据库中获取");
            if (null != dictEntry) {
                value = dictEntry.getName();
                redisUtils.set(redisKey, dictEntry);
            }
        }
        return value;
    }
}